{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fit.datamodules.tomo_rec import MNIST_TRecFITDM\n",
    "from fit.utils.tomo_utils import get_polar_rfft_coords_2D, get_polar_rfft_coords_sinogram\n",
    "from fit.utils import denormalize, convert2DFT\n",
    "from fit.modules import TRecTransformerModule\n",
    "\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "import torch\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "from pytorch_lightning import Trainer, seed_everything\n",
    "from pytorch_lightning.callbacks import ModelCheckpoint\n",
    "\n",
    "import wget\n",
    "from os.path import exists"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Global seed set to 22122020\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "22122020"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seed_everything(22122020)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "dm = MNIST_TRecFITDM(root_dir='./datamodules/data/', batch_size=32, num_angles=7)\n",
    "# FIT: TRec + FBP vs FIT: TRec\n",
    "with_fbp = False\n",
    "\n",
    "dm.prepare_data()\n",
    "dm.setup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "angles = dm.gt_ds.get_ray_trafo().geometry.angles\n",
    "det_len = dm.gt_ds.get_ray_trafo().geometry.detector.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "img_shape = dm.gt_shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "proj_r, proj_phi, src_flatten = get_polar_rfft_coords_sinogram(angles=angles, \n",
    "                                                               det_len=det_len)\n",
    "target_r, target_phi, dst_flatten, order = get_polar_rfft_coords_2D(img_shape=img_shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_heads = 8\n",
    "d_query = 32\n",
    "model = TRecTransformerModule(d_model=n_heads * d_query, \n",
    "                              sinogram_coords=(proj_r, proj_phi),\n",
    "                              target_coords=(target_r, target_phi),\n",
    "                              src_flatten_coords=src_flatten, \n",
    "                              dst_flatten_coords=dst_flatten, \n",
    "                              dst_order=order,\n",
    "                              angles=angles, \n",
    "                              img_shape=img_shape,\n",
    "                              detector_len=det_len,\n",
    "                              loss='prod', \n",
    "                              use_fbp=with_fbp, \n",
    "                              init_bin_factor=1, \n",
    "                              bin_factor_cd=5,\n",
    "                              lr=0.0001, \n",
    "                              weight_decay=0.01, \n",
    "                              attention_type='linear', \n",
    "                              n_layers=4,\n",
    "                              n_heads=n_heads, \n",
    "                              d_query=d_query, \n",
    "                              dropout=0.1, \n",
    "                              attention_dropout=0.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "GPU available: True, used: True\n",
      "TPU available: False, using: 0 TPU cores\n"
     ]
    }
   ],
   "source": [
    "trainer = Trainer(max_epochs=300, \n",
    "                  gpus=1,\n",
    "                  checkpoint_callback=ModelCheckpoint(\n",
    "                                            dirpath=None,\n",
    "                                            save_top_k=1,\n",
    "                                            verbose=False,\n",
    "                                            save_last=True,\n",
    "                                            monitor='Train/avg_val_mse',\n",
    "                                            mode='min',\n",
    "                                            prefix='best_val_loss_'\n",
    "                                        ), \n",
    "                  deterministic=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Uncomment the next line if you want to train your own model. \n",
    "# trainer.fit(model, datamodule=dm);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not exists('./models/trec_mnist/mnist_trec.ckpt'):\n",
    "    wget.download('https://cloud.mpi-cbg.de/index.php/s/CWzpaYelI3lgkb8/download',\n",
    "                  out='./models/trec_mnist/mnist_trec.ckpt')\n",
    "    \n",
    "if not exists('./models/trec_mnist/mnist_trec_fbp.ckpt'):\n",
    "    wget.download('https://cloud.mpi-cbg.de/index.php/s/gHkhiuvqO90amdc/download',\n",
    "                  out='./models/trec_mnist/mnist_trec_fbp.ckpt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "if with_fbp:\n",
    "    path = './models/trec_mnist/mnist_trec_fbp.ckpt'\n",
    "else:\n",
    "    path = './models/trec_mnist/mnist_trec.ckpt'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = TRecTransformerModule.load_from_checkpoint(path, \n",
    "                                               sinogram_coords=(proj_r, proj_phi),\n",
    "                              target_coords=(target_r, target_phi),\n",
    "                              src_flatten_coords=src_flatten, \n",
    "                              dst_flatten_coords=dst_flatten, \n",
    "                              dst_order=order, angles=angles, strict=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]\n",
      "/home/tbuchhol/Programs/miniconda3/envs/fit_test/lib/python3.7/site-packages/pytorch_lightning/utilities/distributed.py:52: UserWarning: The dataloader, test dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 56 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n",
      "  warnings.warn(*args, **kwargs)\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e2a51bba65bb4b78a91b1928d2a07796",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Testing: 0it [00:00, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/tbuchhol/.local/lib/python3.7/site-packages/torch/nn/functional.py:1628: UserWarning: nn.functional.tanh is deprecated. Use torch.tanh instead.\n",
      "  warnings.warn(\"nn.functional.tanh is deprecated. Use torch.tanh instead.\")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bin_factor set to 1.\n",
      "--------------------------------------------------------------------------------\n",
      "DATALOADER:0 TEST RESULTS\n",
      "{'Mean PSNR': array(27.901992797851562, dtype=float32),\n",
      " 'SEM PSNR': array(0.026088180020451546, dtype=float32)}\n",
      "--------------------------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "test_res = trainer.test(model, datamodule=dm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "for x_fc, fbp_fc, y_fc, y_real, (amp_min, amp_max) in dm.test_dataloader():\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.cpu();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/tbuchhol/Gitrepos/FourierImageTransformer/fit/modules/TRecTransformerModule.py:357: UserWarning: Casting complex values to real discards the imaginary part (Triggered internally at  /pytorch/aten/src/ATen/native/Copy.cpp:162.)\n",
      "  pred_fc_[:, :tmp.shape[1]] = tmp\n"
     ]
    }
   ],
   "source": [
    "pred_img, pred_img_before_conv = model.get_imgs(x_fc, fbp_fc, y_fc, amp_min, amp_max)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Before the projection we normalized the image, now we undo this for the visualization.\n",
    "# After denormalization we set all pixels outside of the projection-area to zero\n",
    "pred_img = denormalize(pred_img, dm.mean, dm.std) * dm.__get_circle__()\n",
    "y_real = denormalize(y_real, dm.mean, dm.std) * dm.__get_circle__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "dft_fbp = convert2DFT(x=fbp_fc[:,model.dst_flatten_order], amp_min=amp_min, amp_max=amp_max,\n",
    "                              dst_flatten_order=model.dst_flatten_order, img_shape=model.hparams.img_shape)\n",
    "fbp_img = torch.roll(torch.fft.irfftn(model.mask * dft_fbp[0], s=2 * (model.hparams.img_shape,)),\n",
    "                     2 * (model.hparams.img_shape // 2,), (0, 1))\n",
    "\n",
    "fbp_img = (fbp_img - fbp_img.min())*255/(fbp_img.max() - fbp_img.min())\n",
    "fbp_img = fbp_img * dm.__get_circle__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x360 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(15,5))\n",
    "plt.subplot(1,3,1)\n",
    "plt.imshow(fbp_img, cmap='gray', vmin=y_real[0].min(), vmax=y_real[0].max())\n",
    "plt.title('Filtered Backprojection');\n",
    "plt.subplot(1,3,2)\n",
    "plt.imshow(pred_img[0].detach(), cmap='gray', vmin=y_real[0].min(), vmax=y_real[0].max())\n",
    "plt.title('Prediction');\n",
    "plt.subplot(1,3,3)\n",
    "plt.imshow(y_real[0], cmap='gray')\n",
    "plt.title('Ground Truth');"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
